package com.gxitsky.common.utils;

import com.gxitsky.common.constants.SysConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.service.model.BindingInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.springframework.util.CollectionUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;

/**
 * WebService操作类
 */
public class WebServiceExecutor {

    /**
     * 请求 WebService 服务
     *
     * @param operationName 接口方法名
     * @param wsdUrl        接口地址
     * @param xmlMap        参数是XML格式时用
     * @param list          参数列表
     * @return String
     * @throws Exception
     */
    public static String requestWebService(String operationName, String wsdUrl, HashMap<String, String> xmlMap, List<Object> list) throws Exception {
        if (StringUtils.isBlank(operationName)) {
            throw new Exception("异常：operationName必填参数为null！");
        }
        if (StringUtils.isBlank(wsdUrl)) {
            throw new Exception("异常：wsdUrl必填参数为null！");
        }
        String result = "";
        //构造参数
        String xmlParam = createParamList(xmlMap);
        list.add(xmlParam);
        Object[] params = list.toArray();
        //请求
        result = invokeRemoteMethod(wsdUrl, operationName, params)[0].toString();
        return result;
    }

    /**
     * 组装XML参数
     *
     * @param paramMap XML参数
     * @return String
     * @throws Exception
     */
    public static String createParamList(HashMap<String, String> paramMap) throws Exception {
        String result = "";
        if (!CollectionUtils.isEmpty(paramMap)) {
            // 创建解析器工厂
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = factory.newDocumentBuilder();
            Document document = db.newDocument();
            // 2、创建根节点request
            String xmlRoot = paramMap.get(SysConstants.XML_ROOT);
            Element xml = document.createElement(xmlRoot);
            //3、拼接各个子节点
            for (String key : paramMap.keySet()) {
                if (!key.equals(SysConstants.XML_ROOT)) {
                    Element paramElement = document.createElement(key);
                    paramElement.setTextContent(paramMap.get(key));
                    xml.appendChild(paramElement);
                }
            }
            document.appendChild(xml);
            //将xml转换成string
            result = createXmlToString(document);
        }
        return result;
    }

    private static String createParamList(String[] params) {
        String result = "";
        try {
            // 创建解析器工厂
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = factory.newDocumentBuilder();
            Document document = db.newDocument();
            // 2、创建根节点rss
            Element request = document.createElement("request");
            for (String param :
                    params) {
                Element paramElement = document.createElement(param);
                paramElement.setTextContent("?");
                request.appendChild(paramElement);
            }
            document.appendChild(request);
            //获取一个新的实例
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            //创建一个 Transformer，可以将 XML 文档转换成其他格式
            //有异常抛出，用 try catch 捕获
            Transformer transformer = transformerFactory.newTransformer();
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(document), new StreamResult(writer));
            //最后将 StringWriter 转换为 字符串
            //输出只有一行，是纯粹的XML内容，丢失了换行符、缩进符
//            System.out.println(writer.toString());
//            System.out.println(createXmlToString(document));
            result = createXmlToString(document);
            //截取只返回需要的部分
            result = result.substring(result.indexOf("<request>"), result.length());
        } catch (Exception e) {

        }
        return result;
    }

    /**
     * XML文档转XML字符串
     *
     * @param document XML文档对象
     * @return String
     */
    private static String createXmlToString(Document document) {
        String xmlString = null;
        try {
            // 创建TransformerFactory工厂对象
            TransformerFactory transFactory = TransformerFactory.newInstance();
            // 通过工厂对象, 创建Transformer对象
            Transformer transformer = transFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            //使Xml自动换行, 并自动缩进
            transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");  //中间的参数网址固定写法(这里还没搞懂)
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");                          //是否设置缩进（indent: yes|no）
            // 创建DOMSource对象并将Document加载到其中
            DOMSource domSource = new DOMSource(document);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 使用Transformer的transform()方法将DOM树转换成XML
            transformer.transform(domSource, new StreamResult(bos));
            xmlString = bos.toString();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
        return xmlString;
    }

    /**
     * 调用远程WebService接口
     *
     * @param url           接口地址
     * @param operationName 接口方法名
     * @param parameters    接口参数列表
     * @return Object[]
     * @throws Exception
     */
    public static Object[] invokeRemoteMethod(String url, String operationName, Object[] parameters) throws Exception {
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        if (!url.endsWith("wsdl")) {
            url += "?wsdl";
        }
        org.apache.cxf.endpoint.Client client = dcf.createClient(url);
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy policy = new HTTPClientPolicy();
        policy.setConnectionTimeout(10 * 1000); //连接超时时间
        policy.setReceiveTimeout(12 * 1000);//请求超时时间.
        conduit.setClient(policy);
        //处理webService接口和实现类namespace不同的情况，CXF动态客户端在处理此问题时，会报No operationName was found with the name的异常
        Endpoint endpoint = client.getEndpoint();
        QName opName = new QName(endpoint.getService().getName().getNamespaceURI(), operationName);
        BindingInfo bindingInfo = endpoint.getEndpointInfo().getBinding();
        if (bindingInfo.getOperation(opName) == null) {
            for (BindingOperationInfo operationInfo : bindingInfo.getOperations()) {
                if (operationName.equals(operationInfo.getName().getLocalPart())) {
                    opName = operationInfo.getName();
                    break;
                }
            }
        }
        Object[] res = null;
        res = client.invoke(opName, parameters);
        return res;
    }


    public static void main(String[] args) throws Exception {
        test1();
    }

    private static void test0() throws Exception {
        String url = "http://localhost:8080/helloService/sayHello?wsdl";
        String method = "sayHello";
        Object[] parameters = new Object[]{"我是参数"};
//        System.out.println(invokeRemoteMethod(url, method, parameters)[0]);
    }

    private static void test1() throws Exception {
        Object[] parameters = new Object[5];
        String url = "http://192.168.24.145:8081/himapws/CallServicePort?wsdl";
        String method = "callService";
//        Object[] parameters = new Object[]{"89757","1","2020-04-18","1","宝安人民医院","11","宝安区","222","商务部","3333","false"};
//        Object[] arg = new Object[]{"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"};
        parameters[0] = "";
        parameters[1] = "PROC_GET_DEPT";
        parameters[2] = createParamList(new String[]{"CARD_NO", "CARD_TYPE", "DATE", "HOSPITAL_NO"
                , "HOSPITAL_NAME", "AREA_NO", "AREA_NAME", "DEPT_NO", "DEPT_NAME", "USER_NO", "EXT"});
        parameters[3] = "admin";
        parameters[4] = "fr4t3t1y6s6s1e0502c4d5";
        System.out.println(invokeRemoteMethod(url, method, parameters)[0]);

    }

}
